/*
Creation date:	from 29/04/2003 to 01/04/2003

Autor:			Mountaniol Vsevolod <sebastian@vboxcomm.com>

File:			updater.c

Description:	Create updates for XLT / XR1 systems. 
                Also apply update on target machines.
*/

#include "updater.w.h"
#include "updater.w.memory.h"
#include "updater.w.file.h"
#include "updater.w.system.h"
#include "updater.types.h"
#include "updater.h"
#include "updater.decode.h"
#include "updater.utils.h"
#include "updater.version.h"



/*
    FUNCTION:	TestPacketHeader
    IN:			ucore_t * ucore, packet_header_t * pheader
    OUT:		OK if the header is valid.

    Test validiry of Magic Word. Test version of packet's packager.
    Emits the warning if this updater is elger then packet's updater.
    
*/

INT TestPacketHeader(ucore_t * ps_ucore, packet_header_t * ps_pheader)
{
    ENTRY();

    /* 1. Test magic word */

    if ( w_memcmp(ps_pheader->magic, PACKET_MAGIC_WORD, PACKET_MAGIC_SIZE) )
    {
        RETURN(-EFIELD);
    }

    /* Test Updader Version. Print warning, it current version elder then packet's packer version is. */


    if ( ps_pheader->version < UPDATER_VERSION )
    {
        DE("Warning: Packet is created by newer updater\n");
    }

    /*	TODO
    pheader->control_sum;
    pheader->reserved;
    */

    RETURN(OK);
}



/*
    FUNCTION:   decompress_single_file
    IN:         ucore structure, 
*/
#if(0)
INT decompress_single_file(ucore_t * ps_ucore, )
{

}
#endif


/*
    FUNCTION:	UnpackFile
    IN:			ucore_t *
    OUT:		OK if file unpacked.
    Open file_in name (compressed file). Open it. Extract file by file
    and call function __decompress_file which actually decompress ones.
*/

INT UnpackPackage(ucore_t * ps_ucore, CHAR * pc_packet_name)
{

    file_header_t s_fileheader;
    packet_header_t s_pheader;
    INT i_rv;
    INT i_eof;
    CHAR * pc_dirname;
    size_t i_read_size, i;


    ENTRY();
    
    pc_dirname = w_malloc(sizeof(CHAR) * FH_PATH_SIZE);
    
    if(NULL == pc_dirname)
    {
	    DE("Can't allocate buffer\n");
	    RETURN(-1);
    }

    /* 1.1 Test arguments. */

    if ( NULL == ps_ucore || NULL == pc_packet_name )
    {
        DE( "Error: one of arguments is NULL. ps_ucore= %p, pc_packet_name =%p\n", (void *)ps_ucore, (void *) pc_packet_name );
		w_free(pc_dirname);
        RETURN( -EARGUMENT );
    }

    /* 1.2 Open input file */
    ps_ucore->file_in = w_open(pc_packet_name, W_O_RDONLY);

    if ( w_filestatus(ps_ucore->file_in) < 0  )
    {
        DE("Can't open input file\n");
		w_free(pc_dirname);
        RETURN( -EFILEOPEN );
    }


    /* 1.3 packet header. Test suitability. */

    if ( sizeof(packet_header_t) != w_read(ps_ucore->file_in,(CHAR *)  &s_pheader, sizeof(packet_header_t) ) )
    {
        DE("Error on packet header reading.\n");
		w_free(pc_dirname);
		w_close(ps_ucore->file_in);
        RETURN( -ERR );
    }

    /* Allocate buffers */

    i_rv = AllocateInOutBuffers( ps_ucore, DEFAULT_BUFF_BIG, DEFAULT_BUFF_BIG );

    if ( i_rv )
    {
	    w_free( pc_dirname );
		w_close(ps_ucore->file_in);
	    RETURN( i_rv );
    }

    /* 1.4 Read first file header */

    i_eof = 0;
    while ( ! i_eof )
    {

        if ( sizeof(file_header_t) != w_read(ps_ucore->file_in, (CHAR *) &s_fileheader, sizeof(file_header_t)) )
        {
            DE("Error on header reading.\n");
	    	w_free( pc_dirname );
			w_close(ps_ucore->file_in);
            RETURN( -ERR );
        }
    
        i_rv = TestPacketHeader(ps_ucore, &s_pheader);
        if ( OK != i_rv )
        {
            DE("Package header is wrong\n");
	    	w_free( pc_dirname );
			w_close(ps_ucore->file_in);
            RETURN(-EFIELD);
        }
    
        /*  Test if the directory exists */
    
        i_rv = ExtractDirName( pc_dirname , s_fileheader.target_path);
    
        if ( i_rv )
        {
            PERROR("Error: can't extract directory name");
	    	w_free( pc_dirname );
			w_close(ps_ucore->file_in);
            RETURN(-1);
            /* TODO: A flag for forced unpacking to be added. Then the extracting may be continued. */
        }
    
        i_rv = CreatePath( pc_dirname );
    
        if ( i_rv )
        {
            PERROR("Error: can't create path");
	    	w_free( pc_dirname );
			w_close(ps_ucore->file_in);
            RETURN(-1);
            /* TODO: When flag "forsed" will be added here must be redesigned*/
        }
    
        /* Open output file */
        ps_ucore->file_out = w_open(s_fileheader.target_path, W_O_WRONLY);
        if ( w_filestatus(ps_ucore->file_out) )
        {
            PERROR("Couldn't open the ouput file: ");
	    	w_free( pc_dirname );
			w_close(ps_ucore->file_in);
            RETURN(-EFILEOPEN);
        }
    
        /* Now read the file */
    
        i_read_size = s_fileheader.size;
        while ( i_read_size > 0 )
        {
            i = ((size_t) *ps_ucore->buffers.size_in < i_read_size) ? (size_t) *ps_ucore->buffers.size_in : i_read_size;
            DD("i = %i\n", i);
            i_rv = w_read(ps_ucore->file_in, ps_ucore->buffers.buffer_in, i);

            /* If it red less then asked, sign the end of the file */
            if ( i_rv < i )
            {
                i_eof = 1;
            }

            if ( i_rv <=0 )
            {
				PERROR("Error on reading from input: ");
				w_free( pc_dirname );
				w_close(ps_ucore->file_in);
				w_close(ps_ucore->file_out);
                RETURN(-1);
                /* TODO: Close all the descriptors, remove the file */
            }
            i_read_size -= i_rv;
    
            /* HOOK */
            /* CrossIOBuffers(ps_ucore); */ /* TODO: WARNING: wnen the hoks will be inserted - remove this crossing from here!*/
    
            /* ps_ucore->hooks.hook_decode(ps_ucore, OK); */
    
            i_rv = w_write (ps_ucore->file_out, ps_ucore->buffers.buffer_in, i );
            if ( i_rv != i )
            {
                PERROR("Error on wrinting to out buffer: ");
				w_free( pc_dirname );
				w_close(ps_ucore->file_in);
				w_close(ps_ucore->file_out);
                RETURN(-1);    
            }
        }
    }

    w_close(ps_ucore->file_out);
	w_close(ps_ucore->file_in);

    /* Now extract the file */

    w_free( pc_dirname );
    RETURN( OK );
}

/*
    FUNCTION:	decompress_file
    IN:			CHAR *
    OUT:		int
    Get file name (compressed file). Open it. Extract file by file
    and call function __decompress_file which actually decompress ones.
 
 */
#if JOPA
INT decompress_file (CHAR * pc_file_name_in)
{
    INT i_rv;
	FD_T * i_fd_in;
	FD_T * i_fd_out;
    struct file_header * ps_fh;
    size_t i_offset;
    CHAR * pc_buf;
    CHAR * pc_file_name_out = "/tmp/XXXXXX";
    ENTRY();

    /*    If inpt file name is provided? */
    if ( pc_file_name_in == NULL )
    {
        RETURN( - EINVAL );
    }

    /*	open the file */
    i_fd_in = w_open ( pc_file_name_in, W_O_RDONLY);

    if ( w_filestatus(i_fd_in) < 0 )
    {
        RETURN( - EIO );
    }

    /*	allocate memory for the file header */
    ps_fh = w_malloc ( sizeof ( struct file_header ) );
    if ( ps_fh == NULL )
    {
		w_close(i_fd_in);
        RETURN( - ENOMEM );
    }

    /*	allocate memory for buffer */
    /*	TODO - change to use buffer size from user */
    pc_buf = (CHAR *) w_malloc ( (unsigned int)DEFAULT_BUFF_BIG);
    if ( pc_buf == NULL )
    {
		w_free( ps_fh );
		w_close(i_fd_in);
        PERROR("Low memory");
        RETURN( - ENOMEM );
    }

    /*	read the header */
    i_rv = w_read(i_fd_in, (CHAR *) ps_fh, sizeof(struct file_header));
    if ( i_rv != sizeof(struct file_header) )
    {
		w_free( ps_fh );
		w_free( pc_buf );
		w_close(i_fd_in);
        PERROR("Can't read file header");
        RETURN( - EIO );
    }

    /*    Preinstall it is a script to be run before installation. */
    if ( CHECK_FLAG(ps_fh->flags, UPD_HAS_PREINSTALL) )
    {

        PRINT("Run preinstall: %d\n" , UPD_HAS_PREINSTALL);
        i_rv = w_system(PREINSTALL_SCRIPT_NAME);
        if ( i_rv != 0 )
        {
			w_free( ps_fh );
			w_free( pc_buf );
			w_close(i_fd_in);
            PRINT("preinstall script return error: %i\n", i_rv);
            RETURN( i_rv );
        }
        PRINT("End preinstall\n");
    }

    do
    {

        /*read the len of file */
        i_rv = w_read (i_fd_in, (CHAR *) &i_offset, sizeof(i_offset));
        if ( i_rv != sizeof(i_offset) )
        {
            /*	probably end of file */
            continue;
        }

        /*	get a name for a temporary file */
#if 0
        pc_file_name_out = tempnam("/tmp/", NULL);
        if ( pc_file_name_out == NULL )
        {
            PERROR("Can't generate a name for a temporary file");
            RETURN( - errno );
        }

        DD("Temporary name: %s\n", pc_file_name_out);

        /* 	open the temporary file*/
        i_fd_out = open (pc_file_name_out, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRWXG | S_IRGRP);
#endif /* 0 */	
	
		i_fd_out = w_mkstemp (pc_file_name_out);
        if ( NULL == i_fd_out  )
        {
			w_free( ps_fh );
			w_free( pc_buf );
			w_close(i_fd_in);
            PERROR("Can't open temporary file");
            RETURN( - EIO );
        }

        {
            INT _i, _len, _rv;
            for ( _i = 0; _i < i_offset ; )
            {

                /*	count len to read / write */
                /* TODO - change to use buffer size from user */
                _len = (i_offset - _i> DEFAULT_BUFF_BIG)? DEFAULT_BUFF_BIG : i_offset - _i;


                /*	read pc_buf */ 
                _rv = w_read(i_fd_in, pc_buf, _len);
                if ( _rv != _len )
                {
					w_free( ps_fh );
					w_free( pc_buf );
					w_close(i_fd_in);
					w_close(i_fd_out);
                    PERROR("Can't read from input file - error") ;
                    DD("Asked: %i, read: %i",_len,_rv);
                    RETURN( - EIO );
                }

                /*	write pc_buf */
                _rv = w_write (i_fd_out, pc_buf, _len);
                if ( _rv != _len )
                {
					w_free( ps_fh );
					w_free( pc_buf );
					w_close(i_fd_in);
					w_close(i_fd_out);
                    PERROR("Can't write into output file - error");
                    RETURN( - EIO );
                }

                _i += _len;


            } /* end for */
        }

        w_close (i_fd_out);
        __decompress_file(pc_file_name_out);


    } while ( i_rv > 0 );

    w_close (i_fd_in);
    if ( CHECK_FLAG(ps_fh->flags, UPD_HAS_POSTINSTALL) )
    {
        PRINT("Run postinstall\n");
        i_rv = w_system(POSTINSTALL_SCRIPT_NAME);
        if ( i_rv != 0 )
        {
            PRINT("postinstall script return error code: %i",i_rv);
        }
        PRINT("End postinstall\n");
    }

/*  TFREE(ps_fh); */

	w_free( ps_fh );
	w_free( pc_buf );
    RETURN( 0 );
}


/* 
    FUNCTION: 	__decompress_file
    IN: 		file name.
    OUT: 		status, where 0 - success, else - error.
    Decompress file. Get file 'compressed_file'. 
    Decompress it into 'compressed_file.cmp'.
    After this rename 'compressed_file.cmp' into his original name wich saved
    in header when file was compressed.
*/

#define FREE_BUFS() do{ w_free(pc_buff_big); w_free(pc_buff_little); }while(0)

INT __decompress_file(CHAR * pc_file_name_in)
{

    CHAR *pc_file_name_out;
    CHAR *pc_dirname;
    Bytef * pc_buff_little;
    Bytef * pc_buff_big;
    FD_T * ps_fd_in; 
	FD_T * ps_fd_out;
	INT i_rv;
	INT i_rv_out;
    ULONG ul_not_compressed_data_size;
    ULONG ul_compressed_data_size;
    struct file_header * ps_fh;

    ENTRY();



    /*    construct output file name */
    pc_file_name_out = ConstructName(pc_file_name_in, F_DECOMPRESS);

    if ( pc_file_name_out == NULL )
    {
        RETURN( - ECANCELED );
    }

    /*	TODO - change to use buff size from user */
    pc_buff_little  = (Bytef *) w_malloc(DEFAULT_BUFF_SMALL);
    pc_buff_big = w_malloc ( DEFAULT_BUFF_BIG );

    /*    allocate memory for file header */
    ps_fh = (struct file_header *) w_malloc(sizeof(struct file_header));

    if ( ps_fh == NULL )
    {
        PERROR("Low mem: ");

        FREE_BUFS();
        RETURN( - ENOMEM );
    }

    /*    open input file */
    ps_fd_in = w_open (pc_file_name_in, W_O_RDONLY);
    if ( NULL == ps_fd_in )
    {
        PERROR("Cant open source: ");
        FREE_BUFS();
        RETURN( - EIO );
    }

    /*    open output file */
    ps_fd_out = w_open (pc_file_name_out, W_O_RDWR | W_O_CREAT, W_S_IRUSR | W_S_IWUSR | W_S_IRWXG |  W_S_IRGRP);
    if ( NULL == ps_fd_out )
    {
        FREE_BUFS();
        PERROR("Cant open dest: ");
        RETURN( - EIO );
    }

    /*	read file header */
    i_rv = w_read (ps_fd_in, ps_fh, sizeof(struct file_header) );

    if ( i_rv != sizeof(struct file_header) )
    {
        PERROR("Couldn't read header:" );
        FREE_BUFS();
        RETURN( - EIO );
    }

    /*	check singature */
    /*	TODO - test here control sum - XXX ! */
#if(0)
    if ( CHECK_SINGATURE(ps_fh->singature) != 0 )
    {
        PRINT("Wrong singature!\n");
        FREE_BUFS();
        RETURN( - ECANCELED );
    }
#endif	

    /*	decompress file */
    do
    {

        /*	1. read size of buffer to read */
        i_rv = w_read (ps_fd_in, &ul_compressed_data_size, sizeof(ul_compressed_data_size));
        if ( i_rv != sizeof(ul_compressed_data_size) )
        {

            /*  PERROR("Can't read size of pc_buf; probably end of file:"); */
            continue;
        }

        /*	2. read size of the original uncompressed buffer */
        i_rv = w_read (ps_fd_in, &ul_not_compressed_data_size, sizeof(ul_not_compressed_data_size));
        if ( i_rv != sizeof(ul_not_compressed_data_size) )
        {
            PERROR("Something wrong! Can't read size of not compressed buffer:");
            FREE_BUFS();
            RETURN( - ECANCELED );
        }

        /*	3. and now read the buffer */
        i_rv = w_read (ps_fd_in, pc_buff_little, ul_compressed_data_size);
        if ( i_rv != ul_compressed_data_size )
        {
            PERROR("Something wrong! Can't read compressed buffer:");
            FREE_BUFS();
            RETURN( - ECANCELED );
        }

        /*	decompress data. */
        i_rv_out = uncompress (pc_buff_big, &ul_not_compressed_data_size, pc_buff_little, ul_compressed_data_size);


        /*    check was there error or not */
        switch ( i_rv_out )
        {
        case Z_MEM_ERROR:
            PRINT("Compression memory error\n");
            FREE_BUFS();
            RETURN( - ENOMEM );

        case Z_BUF_ERROR:
            PRINT("Output buffer too small\n");
            FREE_BUFS();
            RETURN( - ENOMEM );

        case  Z_DATA_ERROR:
            PRINT("Input data is corrupted\n");
            FREE_BUFS();
            RETURN( - ECANCELED );

        default:
            /*		PRINT("out: %u\n",(unsigned int)ul_compressed_data_size);	*/
            break;
        }

        /*	write decompressed data to ouput file */
        i_rv_out = w_write (ps_fd_out, pc_buff_big, ul_not_compressed_data_size);

        if ( i_rv_out != ul_not_compressed_data_size )
        {
            PERROR ("Can't write into output file uncompressed data!");
        }



    } while ( i_rv > 0 );
    /*	close files */

    w_close (ps_fd_in);
    w_close (ps_fd_out);

    /* 
       TODO Here test and process flags: 
       UPD_INSTALL_PATH
       
          If set create path - if not exists
          Else if the path not exist brek this file installation
          
       UPD_INSTALL_FORCE
  
          If set then if already exists such file just write new file over.
          Else (not set) it depends on UPD_BACKUP flag:
              If UPD_BACKUP set make reserve copy and write over.
              Else break this file installation.
       
       UPD_BACKUP
  
          If set - backup file before to write this file over.
          
  
       UPD_INSTALL_TARGET
  
          TODO - reserved. In theory this option give ability encript file /
          decript on client machuine. 
     */

    /* if (CHECK_FLAG(ps_fh->flags),UPD_INSTALL_PATH)
    {
    }
    */

    /* Allocate memory for extracter directory name: size of te string + 1 */
    pc_dirname = w_malloc (strlen(ps_fh->target_path) * sizeof(CHAR) + sizeof(CHAR));
    if ( NULL == pc_dirname )
    {
        RETURN(- 1);
    }

    i_rv = ExtractDirName( pc_dirname, ps_fh->target_path );  
    if ( i_rv )
    {
        RETURN(-1);
    }

    /*	rename (or move) decompressed file */
    i_rv = CreatePath(pc_dirname);
    if ( i_rv )
    {
        RETURN(-1);
    }

    w_free( pc_dirname );
    PRINT("rename:%s --> %s\n",pc_file_name_out,ps_fh->target_path);
    i_rv = rename (pc_file_name_out, ps_fh->target_path);

    /*    status of 'rename file' */
    if ( i_rv < 0 )
    {
        PERROR("Can't rename file:");
        FREE_BUFS();
        RETURN( errno );
    }

    /*    change permissions of file */
    i_rv = chmod(ps_fh->target_path, ps_fh->stat.st_mode);
    i_rv = unlink(pc_file_name_in);
    /*    free file header */
    if ( NULL != ps_fh )
    {
        w_free(ps_fh);
    }

    FREE_BUFS();
    /*	return */
    RETURN( 0 );

}

#undef FREE_BUFS

#endif /* JOPA */
